using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;
using OracleInternal.ServiceObjects;

namespace Oracle.ManagedDataAccess.Types
{
	public sealed class OracleRefCursor : MarshalByRefObject, IDisposable, INullable
	{
		internal OracleRefCursorImpl m_refCursorImpl;

		internal OracleConnection m_connection;

		private OracleDataReader m_cachedReader;

		private long m_fetchSize = 65535L;

		private bool m_bNotNull = true;

		private bool m_bReaderGivenToRefCursor;

		internal const int MAX_SIZE = 16;

		private long m_initialLongFS;

		private long m_clientInitialLOBFS;

		private long m_internalInitialLOBFS;

		private long m_internalInitialJSONFS;

		internal long[] m_snapshotSCN;

		internal OracleIntervalDS m_sessionTimeZone;

		internal bool m_bClosed;

		internal bool m_bDisposed;

		private object m_syncObj = new object();

		private object lockRefCursor = new object();

		internal RefCursorInfo m_refCursorInfo;

		public static readonly OracleRefCursor Null = new OracleRefCursor();

		public OracleConnection Connection
		{
			get
			{
				if (m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (!m_bNotNull)
				{
					return null;
				}
				return m_connection;
			}
		}

		public long FetchSize
		{
			get
			{
				if (m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				return m_fetchSize;
			}
			set
			{
				if (m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (value <= 0)
				{
					throw new ArgumentException();
				}
				if (m_cachedReader != null)
				{
					m_cachedReader.FetchSize = value;
				}
				m_fetchSize = value;
			}
		}

		public long RowSize
		{
			get
			{
				if (m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (m_refCursorImpl == null || m_refCursorImpl.m_sqlMetaData == null)
				{
					return 0L;
				}
				return m_refCursorImpl.m_sqlMetaData.m_maxRowSize + m_refCursorImpl.m_sqlMetaData.m_numOfLOBColumns * Math.Max(86L, 86 + m_clientInitialLOBFS) + m_refCursorImpl.m_sqlMetaData.m_numOfJSONColumns * Math.Max(86, 86) + m_refCursorImpl.m_sqlMetaData.m_numOfLONGColumns * Math.Max(2L, m_initialLongFS) + m_refCursorImpl.m_sqlMetaData.m_numOfBFileColumns * 86;
			}
		}

		public bool IsNull => !m_bNotNull;

		private OracleRefCursor()
		{
			m_bNotNull = false;
		}

		internal OracleRefCursor(OracleConnection connection, OracleRefCursorImpl refCursorImpl, OracleIntervalDS sessionTimeZone, string commandText, string paramPosOrName, long initialLongFS, long clientInitialLobFS, long internalInitialLOBFS, long internalInitialJSONFS, long[] scnFromExecution, bool bImplicitRefCursor = false)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.ctor);
			}
			try
			{
				m_connection = connection;
				m_sessionTimeZone = sessionTimeZone;
				m_initialLongFS = initialLongFS;
				m_clientInitialLOBFS = clientInitialLobFS;
				m_internalInitialLOBFS = internalInitialLOBFS;
				m_internalInitialJSONFS = internalInitialJSONFS;
				m_snapshotSCN = scnFromExecution;
				m_refCursorImpl = refCursorImpl;
				bool flag = false;
				int result = -1;
				if (int.TryParse(paramPosOrName, out result))
				{
					flag = true;
				}
				ConfigBaseClass.StoredProcedureInfo storedProcInfo = ConfigBaseClass.GetInstance(bIsManaged: true).GetStoredProcInfo(commandText);
				if (storedProcInfo != null)
				{
					List<RefCursorInfo> list = null;
					list = ((!bImplicitRefCursor) ? storedProcInfo.m_refCursors : storedProcInfo.m_implicitlyRetRefCursors);
					foreach (RefCursorInfo item in list)
					{
						if (flag)
						{
							if (item.position == result)
							{
								m_refCursorInfo = item;
							}
						}
						else if (item.name == paramPosOrName)
						{
							m_refCursorInfo = item;
						}
					}
				}
				if (m_connection.m_oracleConnectionImpl != null)
				{
					m_connection.m_oracleConnectionImpl.RegisterForConnectionClose(this);
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.ctor, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.ctor);
				}
			}
		}

		public OracleDataReader GetDataReader()
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader);
			}
			try
			{
				if (!m_bNotNull)
				{
					throw new OracleNullValueException();
				}
				return GetDataReader(fillRequest: false);
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader);
				}
			}
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal OracleDataReader GetDataReader(bool fillRequest)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader);
			}
			try
			{
				if (m_bClosed)
				{
					throw new InvalidOperationException(OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.OBJECT_CLOSED));
				}
				if (m_bReaderGivenToRefCursor)
				{
					if (fillRequest && m_cachedReader != null)
					{
						return m_cachedReader;
					}
					throw new InvalidOperationException();
				}
				OracleDataReaderImpl initializedDataReaderImpl = m_connection.m_oracleConnectionImpl.GetInitializedDataReaderImpl(m_refCursorImpl.m_accessors, m_refCursorImpl.m_sqlMetaData, m_refCursorImpl.m_cursorId, 0, null, m_sessionTimeZone, m_initialLongFS, m_clientInitialLOBFS, m_internalInitialLOBFS, m_internalInitialJSONFS, m_snapshotSCN);
				initializedDataReaderImpl.m_bHasMoreRowsInDB = true;
				initializedDataReaderImpl.m_bForRefCursor = true;
				OracleDataReader oracleDataReader = new OracleDataReader(initializedDataReaderImpl, m_connection, m_fetchSize, m_clientInitialLOBFS, m_internalInitialLOBFS, m_internalInitialJSONFS, (int)m_initialLongFS, -1, string.Empty, SqlStatementType.PLSQL);
				oracleDataReader.RefCursor = this;
				if (fillRequest)
				{
					m_cachedReader = oracleDataReader;
				}
				else
				{
					m_cachedReader = null;
				}
				m_bReaderGivenToRefCursor = true;
				return oracleDataReader;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.GetDataReader);
				}
			}
		}

		internal void Close()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Close);
			}
			if (!m_bNotNull || m_bClosed)
			{
				return;
			}
			lock (lockRefCursor)
			{
				if (m_bClosed)
				{
					return;
				}
				try
				{
					if (m_connection == null)
					{
						return;
					}
					if (m_cachedReader != null && !m_cachedReader.IsClosed)
					{
						lock (m_syncObj)
						{
							if (m_cachedReader != null)
							{
								m_cachedReader.Close();
								m_cachedReader = null;
							}
						}
					}
					else if (!m_bReaderGivenToRefCursor)
					{
						m_connection.m_oracleConnectionImpl.AddCursorIdToBeClosed(m_refCursorImpl.m_cursorId);
					}
				}
				catch (Exception ex)
				{
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Close, ex.ToString());
					}
				}
				finally
				{
					m_bClosed = true;
					if (m_connection != null)
					{
						if (m_connection.m_oracleConnectionImpl != null)
						{
							m_connection.m_oracleConnectionImpl.DeregisterForConnectionClose(this);
						}
						m_connection = null;
					}
					if (!m_bDisposed)
					{
						Dispose();
					}
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Close);
					}
				}
			}
		}

		internal void ConnectionClose()
		{
			if (!m_bClosed)
			{
				Close();
			}
		}

		public void Dispose()
		{
			if (ProviderConfig.m_bTraceLevelPublic)
			{
				Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose);
			}
			try
			{
				Dispose(disposing: true);
			}
			catch (Exception ex)
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose, ex.ToString());
				}
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose);
				}
			}
		}

		private void Dispose(bool disposing)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Entry, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose);
			}
			if (m_bDisposed)
			{
				return;
			}
			lock (lockRefCursor)
			{
				if (m_bDisposed)
				{
					return;
				}
				try
				{
					if (m_bNotNull)
					{
						m_bDisposed = true;
						if (!m_bClosed)
						{
							Close();
						}
					}
				}
				catch (Exception ex)
				{
					OracleException.HandleError(OracleTraceLevel.Private, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose, ex);
				}
				finally
				{
					GC.SuppressFinalize(this);
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.Exit, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.Dispose);
					}
				}
			}
		}

		~OracleRefCursor()
		{
			try
			{
				Dispose(disposing: false);
			}
			catch (Exception ex)
			{
				if (ProviderConfig.m_bTraceLevelPublic)
				{
					Trace.Write(OracleTraceLevel.Public, OracleTraceTag.Error, OracleTraceClassName.OracleRefCursor, OracleTraceFuncName.dtor, ex.Message);
				}
			}
		}
	}
}
